#include "SocketEndPoint.h"
#include <iostream>

#ifndef SOCKET_H
#define	SOCKET_H

enum TransportProtocol { TCP=6, UDP=17, UNSPECIFIED=-1 };

class Socket
{
private:
    
    static const int CONST_UNINITIALIZED_SOCKET = -1;
    static const int CONST_NO_TIMEOUT = 0;
    
    int socketDescriptor;
    int timeout;
    bool connected;
    bool bound;
    bool closed;
    bool remoteEndPointSet;
    TransportProtocol transportProtocol;   
    SocketEndPoint localEndPoint;
    SocketEndPoint remoteEndPoint;
    istream * inputStream;
    ostream * outputStream;
    
    void initialize();
    bool socketInitialized();
    
    void execCreateSocket();
    void execBind(const SocketEndPoint & localEndPoint);
   
    void handleCreateSocketError(int errorCode);
    void handleBindError(int errorCode);
    void handleConnectError(int errorCode);
    void handleReadWriteError(int errorCode);
    
    void throwExceptionIfArgumentNegative(int arg);
    void throwExceptionIfSocketNotInitialized();
    void throwExceptionIfSocketClosed();
    void throwExceptionIfReadWritePreConditionsFail();
    
    void initializeSockAddrInFromSocketEndPoint(
        const SocketEndPoint & socketEndPoint, struct sockaddr_in * sockAddr);
    
public:
    
    Socket(TransportProtocol transportProtocol);
    
public:
    
    void bindSocket(const SocketEndPoint & localEndPoint);

    void closeSocket();

    void connectToHost(const SocketEndPoint & remoteEndPoint);

    int writeToSocket(void * buffer, int length);

    int readFromSocket(void * bufer, int length);

    const SocketEndPoint & getLocalEndPoint() const;

    const SocketEndPoint & setRemoteEndPoint(const SocketEndPoint & remoteEndPoint) const;
    
    const SocketEndPoint & getRemoteEndPoint() const;

    void setReadTimeout(int timeout);

    int getReadTimeout() const;
    
    TransportProtocol getTransportProtocol() const;

    bool isConnected() const;

    bool isBound() const;
    
    istream & getInputStream() const;

    ostream & getOutputStream() const;
};

#endif

